#include "protocol.h"
#include "server.h"

/* 创建一个监听套接字 */
int retListen(char *MYIP, char *MYPORT)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket()");
		exit(1);
	}
	
	/* 设置同一地址可复用*/
	int val = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(val));

	struct sockaddr_in myaddr;
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(atoi(MYPORT));
	inet_pton(AF_INET, MYIP, &myaddr.sin_addr);
	/* 绑定ip和端口号 */
	if (bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0)
	{
		perror("bind()");
		exit(1);
	}
	
	/* 监听 */
	if (listen(sockfd, 10) < 0)
	{
		perror("listen()");
		exit(1);
	}
	return sockfd;
}

static int unpack(struct FSM_ST *fsm)
{
	u_int8_t ch;
	u_int8_t destOpt[BUFFSIZE] = {0};
	int i = 0;
	
	/* 读取到包头 */
	do
	{
		ch = fsm->recvbuff[i++];
	
	}while (ch != 0xC0);
	
	/* 为防止读到错误的包头*/
	while (ch == 0xC0)
	{
		ch = fsm->recvbuff[i++];
	}

	int k = 0;
	
	/* 解析真正内容 @pkg_len:内容长度 @cmd_no:请求命令*/
	while (ch != 0xC0)
	{
		destOpt[k++] = ch;
		ch = fsm->recvbuff[i++];
	}
	int pkg_len = destOpt[0] | destOpt[1] << 8 | destOpt[2] << 16 | destOpt[3] << 24;

	if (pkg_len != k)
	{
		return -1;
	}
	fsm->cmd_no = destOpt[4] | destOpt[5] << 8 | destOpt[6] << 16 | destOpt[7] << 24;

	return 0;
}

/* pkg_len cmd_no resp_len result filenames  */
static void packet(char *srcbuff, char *destbuff)
{
	int namelen = strlen(srcbuff);
	
	int pkg_len = 4 + 4 + 4 + 1 + namelen;
	enum CMD_NO cmd_no = FTP_CMD_LS;
	int resp_len = namelen + 1;
	
	int i = 0;
	destbuff[i++] = 0xC0;

	destbuff[i++] = pkg_len & 0xff;
	destbuff[i++] = (pkg_len >> 8) & 0xff;
	destbuff[i++] = (pkg_len >> 16) & 0xff;
	destbuff[i++] = (pkg_len >> 24) & 0xff;

	destbuff[i++] = cmd_no & 0xff;
	destbuff[i++] = (cmd_no >> 8) & 0xff;
	destbuff[i++] = (cmd_no >> 16) & 0xff;
	destbuff[i++] = (cmd_no >> 24) & 0xff;

	destbuff[i++] = resp_len & 0xff;
	destbuff[i++] = (resp_len >> 8) & 0xff;
	destbuff[i++] = (resp_len >> 16) & 0xff;
	destbuff[i++] = (resp_len >> 24) & 0xff;
	
	/* 结果 */
	destbuff[i++] = 0;
	
	while (*srcbuff)
	{
		destbuff[i++] = *srcbuff;
		srcbuff++;
	}

	destbuff[i++] = 0xC0;
	destbuff[i] = '\0';
}

static void execLs(int confd)
{
	DIR *dir = opendir(SERVERPATH);	
	if (dir == NULL)
	{
		perror("opendir()");
		exit(1);
	}
	
	struct dirent *dirp;
	int pos = 0;
	u_int8_t filename[NAMESIZE] = {0};
	u_int8_t destname[NAMESIZE] = {0};
	while(dirp = readdir(dir))
	{
		if ((strcmp(dirp->d_name, ".") != 0) && (strcmp(dirp->d_name, "..") != 0))
		{
			pos += sprintf(filename+pos, "%s ", dirp->d_name);
		}
	}
	packet(filename, destname);
	send(confd, destname, NAMESIZE, 0);
	closedir(dir);
}

static void handlerConnection(struct FSM_ST *fsm)
{
	switch (fsm->cmd_no)
	{
		case FTP_CMD_LS:
			execLs(fsm->confd);
			break;
		case FTP_CMD_GET:
			break;
		case FTP_CMD_PUT:
			break;
		case FTP_CMD_BYE:
			break;
		default:
			break;
	}
}

static void driver(struct FSM_ST *fsm)
{
	
	switch (fsm->state)
	{
		case FSM_RECV:
			if (recv(fsm->confd, fsm->recvbuff, BUFFSIZE, 0) < 0)
			{
				fsm->errstr = "recv()";
				fsm->state = FSM_EX;
			}
			else
				fsm->state = FSM_UNPC;
			break;
		case FSM_UNPC:
			if (unpack(fsm) < 0)
			{
				fsm->errstr = "unpack()";
				fsm->state = FSM_EX;
			}
			else
				fsm->state = FSM_REPLY;
			break;
		case FSM_REPLY:
			handlerConnection(fsm);
			fsm->state = FSM_RECV;
			break;
		case FSM_EX:
			break;
		case FSM_T:
			break;
		default:
			break;
	}
}

int main(int argc, char **argv)
{
	if (argc < 3)
	{
		fprintf(stderr, "Usage:\n");
		exit(1);
	}

	int sockfd = retListen(argv[1], argv[2]);

	while (1)
	{
		struct sockaddr_in client;
		socklen_t len = sizeof(client);
		/* 接收一个连接并保存客户端的信息在结构体中 */
		int confd;
		confd = accept(sockfd, (struct sockaddr *)&client, &len);
		if (confd < 0)
		{
			perror("accept()");
			exit(1);
		}
		
		pid_t pid;
		int retlen;
		/* 创建一个子进程完成每个连接的任务 */
		pid = fork();
		if (pid < 0)
		{
			perror("fork()");
			exit(1);
		}
		
		if (pid == 0)
		{
			close(sockfd);
			struct FSM_ST fsm;
			fsm.state = FSM_RECV;
			fsm.confd = confd;
			while (fsm.state != FSM_T)
			{
				driver(&fsm);
			}
			close(confd);
			exit(0);
		}
		close(confd);
	}

	close(sockfd);
	exit(0);
}
